package main

import (
	"shell/common"
	"shell/common/packages"
	"time"

	"github.com/google/uuid"
)

// 处理被控端连接，即处理保活连接
func handleControlledEnd(stream *common.QuicStream, metadata *packages.PackageMetadata) {
	// 如果metadata表示的包不是keepalivereq包，就是非法的
	if metadata.PkgAct != packages.ActKeepaliveReq {
		logger.Errorf(0, "controlled end first package must be keepalived request: %s", metadata.PkgAct.String())
		stream.CloseConn(common.CloseByProcess)
		return
	}
	req := new(packages.KeepaliveReq)
	err := stream.ReadPackageAfterDetect(req, metadata)
	if err != nil {
		common.HandleErr(err)
		stream.CloseConn(common.CloseByProcess)
		return
	}
	resp := packages.NewKeepaliveResp(req.CmdUUID, Role)
	if err := stream.WritePackage(resp); err != nil {
		common.HandleErr(err)
		stream.CloseConn(common.CloseByProcess)
		return
	}
	go func() {
		_ = KeepaliveOnce(stream.Conn)
	}()
	conn := stream.Conn
	stream.WaitForClose(Role, false)

	_, err = SyncControlledEnd(req, conn)
	if err != nil {
		common.HandleErr(err)
		conn.Close(common.CloseByProcess)
		return
	}
	stopChan := conn.Ctx().Done()
	for {
		select {
		case <-stopChan:
			return
		default:
			stream, err := conn.AcceptStream()
			if err != nil {
				common.HandleErr(err)
				return
			}
			md, err := stream.DetectPackage()
			if err != nil {
				common.HandleErr(err)
				return
			}
			if md.PkgAct == packages.ActCloseConnReq {
				// 主动断开连接
				req := new(packages.CloseConnReq)
				err := stream.ReadPackageAfterDetect(req, md)
				if err != nil {
					common.HandleErr(err)
					stream.CloseConn(common.CloseByProcess)
					return
				}
				resp := req.ToResp(Role, true)
				err = stream.WritePackage(resp)
				if err != nil {
					common.HandleErr(err)
					stream.CloseConn(common.CloseByProcess)
					return
				}
				time.Sleep(100 * time.Millisecond)
				stream.CloseConn(common.CloseByProcess)
				return
			}
			req := new(packages.KeepaliveReq)
			err = stream.ReadPackageAfterDetect(req, md)
			if err != nil {
				common.HandleErr(err)
				stream.CloseConn(common.CloseByProcess)
				return
			}
			resp := packages.NewKeepaliveResp(req.CmdUUID, Role)
			err = stream.WritePackage(resp)
			if err != nil {
				common.HandleErr(err)
				stream.CloseConn(common.CloseByProcess)
				return
			}
			_, err = SyncControlledEnd(req, stream.Conn)
			if err != nil {
				common.HandleErr(err)
				stream.CloseConn(common.CloseByProcess)
				return
			}
			go func() {
				err := KeepaliveOnce(stream.Conn)
				if err != nil {
					common.HandleErr(err)
				}
			}()
			stream.WaitForClose(Role, false)
		}
	}
}

// 向被控端发送保活请求
func KeepaliveOnce(conn *common.QuicConn) error {
	stream, err := conn.OpenStream()
	if err != nil {
		return err
	}
	req := new(packages.KeepaliveReq)
	req.CmdUUID = uuid.New()
	req.Role = Role
	err = stream.WritePackage(req)
	if err != nil {
		stream.CloseConn(common.CloseByProcess)
		return err
	}
	resp := new(packages.KeepaliveResp)
	err = stream.ReadPackage(resp)
	if err != nil {
		stream.CloseConn(common.CloseByProcess)
		return err
	}
	if resp.CmdUUID != req.CmdUUID || resp.GetRole() != packages.RoleControlled {
		logger.Errorf(0, "error get keepalive response")
	}
	_ = stream.CloseStreamSync(Role, true)
	return nil
}
